// ----------------------------------
// projects/c++/integer/TestInteger.h
// Copyright (C) 2009
// Glenn P. Downing
// ----------------------------------

#ifndef TestInteger_h
#define TestInteger_h

// --------
// includes
// --------

#include <algorithm> // equal
#include <cstring>   // strcmp
#include <sstream>   // ostringstream
#include <stdexcept> // invalid_argument
#include <string>    // ==

#include "cppunit/TestFixture.h"             // TestFixture
#include "cppunit/extensions/HelperMacros.h" // CPPUNIT_TEST, CPPUNIT_TEST_SUITE, CPPUNIT_TEST_SUITE

#include "Integer.h"

// -----------
// TestInteger
// -----------

struct TestInteger : CppUnit::TestFixture {
    // ----------------
    // test_constructor
    // ----------------

	void test_constructor_1 () {
        try {
            const My::Integer<int> x("abc");
            CPPUNIT_ASSERT(false);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(strcmp(e.what(), "My::Integer()"));}}

    void test_constructor_2 () {
        try {
            const My::Integer<int> x("2");}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_constructor_3 () {
        try {
            const My::Integer<int> x = 2;}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_constructor_4 () {
        try {
            const My::Integer<int> x("-2");}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_constructor_5 () {
        try {
            const My::Integer<int> x("68465435154");}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_constructor_6 () {
        try {
            const My::Integer<int> x("-864354384834");}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_constructor_7 () {
        try {
            const My::Integer<int> x(6835154);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_constructor_8 () {
        try {
            const My::Integer<int> x = -864334;}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}


    // --------
    // test_abs
    // --------

    void test_abs () {
        try {
            const My::Integer<int> x = -98765;
            const My::Integer<int> y = x.abs();
            CPPUNIT_ASSERT(y == 98765);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    // -------------------
    // test_divides_digits
    // -------------------

	void test_divides_digits1 () {
        const int a[] = {7, 7, 6, 2, 3, 1};
        const int b[] = {7, 6, 5};
        const int c[] = {3, 3, 2};
              int x[10];
        const int* p = My::divides_digits(a, a + 6, b, b + 3, x);
        CPPUNIT_ASSERT(p - x == 3);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

	void test_divides_digits2 () {
        const int a[] = {8, 7, 6, 2, 3, 1};
        const int b[] = {7, 6, 5};
        const int c[] = {4, 3, 2};
              int x[10];
        const int* p = My::divides_digits(a, a + 6, b, b + 3, x);
        CPPUNIT_ASSERT(p - x == 3);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

    // -------------
    // test_equal_to
    // -------------

    void test_equal_to1 () {
        try {
            const My::Integer<int> x = 98765;
            const My::Integer<int> y = 98765;
            CPPUNIT_ASSERT(      x == y);
            CPPUNIT_ASSERT(      x == 98765);
            CPPUNIT_ASSERT(  98765 == x);
            CPPUNIT_ASSERT(    !(x != y));
            CPPUNIT_ASSERT(    !(x != 98765));
            CPPUNIT_ASSERT(!(98765 != y));}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_equal_to2 () {
        try {
            const My::Integer<int> x = 98765;
            const My::Integer<int> y = 0;
            CPPUNIT_ASSERT(      x != y);
            CPPUNIT_ASSERT(      y != 98765);
            CPPUNIT_ASSERT(  0 != x);
            CPPUNIT_ASSERT(    !(x == y));
            CPPUNIT_ASSERT(    !(x == 0));
            CPPUNIT_ASSERT(!(0 != y));}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}


    // -------------
    // test_less_than
    // -------------

    void test_less_than1 () {
        try {
            const My::Integer<int> x = 1;
            const My::Integer<int> y = 6;
            CPPUNIT_ASSERT(      x < y);
            CPPUNIT_ASSERT(      x < 6);
            CPPUNIT_ASSERT(  6 > x);
            CPPUNIT_ASSERT(    !(y < x));
            CPPUNIT_ASSERT(    !(x > y));
            CPPUNIT_ASSERT(    (x <= 1));
            CPPUNIT_ASSERT((6 <= y));}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_less_than2 () {
        try {
            const My::Integer<int> x = -1;
            const My::Integer<int> y = -6;
            CPPUNIT_ASSERT(      x > y);
            CPPUNIT_ASSERT(      x < 6);
            CPPUNIT_ASSERT(  6 > x);
            CPPUNIT_ASSERT(    !(x < y));
            CPPUNIT_ASSERT(    (x <= 1));
            CPPUNIT_ASSERT((-6 <= y));}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}


    // -----------------
    // test_minus_digits
    // -----------------

    void test_minus_digits1 () {
        const int a[] = {1, 0, 8};
        const int b[] = {7, 6, 7};
        const int c[] = {4, 3};
              int x[10];
        const int* p = My::minus_digits(a, a + 3, b, b + 3, x);
        CPPUNIT_ASSERT(p - x == 2);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

    void test_minus_digits2 () {
        const int a[] = {1, 0, 8};
        const int b[] = {7, 6, 5};
        const int c[] = {4, 3, 2};
              int x[10];
        const int* p = My::minus_digits(a, a + 3, b, b + 3, x);
        CPPUNIT_ASSERT(p - x == 3);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

    void test_minus_digits3 () {
        const int a[] = {6, 5, 8};
        const int b[] = {1, 5, 8};
        const int c[] = {5};
              int x[10];
        const int* p = My::minus_digits(a, a + 1, b, b + 1, x);
        CPPUNIT_ASSERT(p - x == 1);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

    void test_minus_digits4 () {
		std::vector<int> a(6);
        int a1[] = {8, 7, 6, 2, 3, 1};
		a.assign(a1,a1+6);
		std::vector<int> b(5);
        const int b1[] = {0,0,7,6,5};
		b.assign(b1, b1+5);
        const int c1[] = {8,7,9,5,7};
		std::vector<int> c(5);
		c.assign(c1, c1+5);

        std::vector<int>::iterator p = My::minus_digits(a.begin(), a.end() , b.begin(), b.end(), a.begin());
        CPPUNIT_ASSERT(p - a.begin() == 5);
        CPPUNIT_ASSERT(std::equal(a.begin(), p, c.begin()));}

	void test_minus_digits5 () {
		std::vector<int> a(5);
        int a1[] = {8,7,2,9,1};
		a.assign(a1,a1+5);
		std::vector<int> b(4);
        const int b1[] = {0,7,6,5};
		b.assign(b1, b1+4);
        const int c1[] = {8,0,6,3,1};
		std::vector<int> c(5);
		c.assign(c1, c1+5);

        std::vector<int>::iterator p = My::minus_digits(a.begin(), a.end() , b.begin(), b.end(), a.begin());
        CPPUNIT_ASSERT(p - a.begin() == 5);
        CPPUNIT_ASSERT(std::equal(a.begin(), p, c.begin()));}

    void test_minus_digits6 () {
        const int a[] = {6, 5, 8};
        const int b[] = {6, 5, 8};
        const int c[] = {0};
              int x[10];
        const int* p = My::minus_digits(a, a + 3, b, b + 3, x);
        CPPUNIT_ASSERT(p - x == 1);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

    // ----------------------
    // test_multiplies_digits
    // ----------------------

    void test_multiplies_digits () {
        const int a[] = {4, 3, 2};
        const int b[] = {7, 6, 5};
        const int c[] = {8, 7, 6, 2, 3, 1};
              int x[10];
        const int* p = My::multiplies_digits(a, a + 3, b, b + 3, x);
        CPPUNIT_ASSERT(p - x == 6);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

	void test_multiplies_digits1 () {
        const int a[] = {2};
        const int b[] = {3};
        const int c[] = {6};
              int x[10];
        const int* p = My::multiplies_digits(a, a + 1, b, b + 1, x);
        CPPUNIT_ASSERT(p - x == 1);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

	void test_multiplies_digits2 () {
        const int a[] = {6,2};
        std::vector<int> b(10); int b_contents[] = {1,4};
        b.assign(b_contents,b_contents+2);
        std::vector<int> c(10); int c_contents[] = {6,6,0,1};
        c.assign(c_contents,c_contents+4);

        const std::vector<int>::iterator p = My::multiplies_digits( b.begin(), b.end(),a, a + 2, b.begin());

        CPPUNIT_ASSERT(p - b.begin() == 4);
        CPPUNIT_ASSERT(std::equal(b.begin(), p, c.begin()));}


    // -------------
    // test_negation
    // -------------

    void test_negation () {
        try {
            const My::Integer<int> x = 98765;
            const My::Integer<int> y = -x;
            CPPUNIT_ASSERT(y == -98765);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    // -----------
    // test_output
    // -----------

    void test_output1 () {
        try {
            const My::Integer<int> x = 98765;
            std::ostringstream out;
            out << x;
            CPPUNIT_ASSERT(out.str() == "98765");}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_output2 () {
        try {
            const My::Integer<int> x = -98765;
            std::ostringstream out;
            out << x;
            CPPUNIT_ASSERT(out.str() == "-98765");}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    // ----------------
    // test_plus_digits
    // ----------------

    void test_plus_digits () {
            const int a[] = {4, 3, 8};
            const int b[] = {7, 6, 5};
            const int c[] = {1, 0, 4, 1};
				  int x[10];
            const int* p = My::plus_digits(a, a + 3, b, b + 3, x);
            CPPUNIT_ASSERT(p - x == 4);
            CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

        void test_plus_digits1 () {
            const int a[] = {5};
            const int b[] = {2};
            const int c[] = {7};
                  int x[10];
            const int* p = My::plus_digits(a, a + 1, b, b + 1, x);
            CPPUNIT_ASSERT(p - x == 1);
            CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

        void test_plus_digits2 () {
            const int a[] = {5, 2, 3};
				  int b[] = {0, 2, 1};
			const int c[] = {5, 4, 4};

            const int* p = My::plus_digits(a, a + 3, b, b + 3, b);
            CPPUNIT_ASSERT(p - b == 3);
            CPPUNIT_ASSERT(std::equal(const_cast<const int*>(b), p, c));}

        void test_plus_digits3 () {
            const int a[] = {5, 3, 8};
            const int b[] = {2};
            const int c[] = {7, 3, 8};
                  int x[10];
            const int* p = My::plus_digits(a, a + 3, b, b + 1, x);
            CPPUNIT_ASSERT(p - x == 3);
            CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, c));}

        void test_plus_digits4 () {
            	  int a[] = {5, 3, 8};
            const int b[] = {2};
            const int c[] = {7, 3, 8};
                  
            const int* p = My::plus_digits(a, a + 3, b, b + 1, a);
            CPPUNIT_ASSERT(p - a == 3);
            CPPUNIT_ASSERT(std::equal(const_cast<const int*>(a), p, c));}

		void test_plus_digits5 () {
			std::vector<int> a(3);
				  int a1[] = {3,3,2};
			a.assign(a1,a1+3);
			std::vector<int> b(1);
			const int b1[] = {1};
			b.assign(b1, b1+1);
			const int c1[] = {4,3,2};
			std::vector<int> c(3);
			c.assign(c1, c1+3);

			std::vector<int>::iterator p = My::plus_digits(a.begin(), a.end() , b.begin(), b.end(), a.begin());
			CPPUNIT_ASSERT(p - a.begin() == 3);
			CPPUNIT_ASSERT(std::equal(a.begin(), p, c.begin()));}
		
    // --------
    // test_pow
    // --------

    void test_pow1 () {
        try {
            const My::Integer<int> x = 5;
            const int              e =  -2;
            const My::Integer<int> y = x.pow(e);
            CPPUNIT_ASSERT(false);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(true);}}

    void test_pow2 () {
        try {
            const My::Integer<int> x = 0;
            const int              e =  0;
            CPPUNIT_ASSERT(x == 0);
            const My::Integer<int> y = x.pow(e);
            CPPUNIT_ASSERT(false);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(true);}}

    void test_pow3 () {
        try {
            const My::Integer<int> x = 50;
            const int              e =  5;
            const My::Integer<int> y = x.pow(e);
            CPPUNIT_ASSERT(y ==  312500000);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_pow4 () {
        try {
            const My::Integer<int> x = 50;
            const int              e =  0;
            const My::Integer<int> y = x.pow(e);
            CPPUNIT_ASSERT(y ==  1);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_pow5 () {
        try {
            const My::Integer<int> x = 4;
            const int              e = 17;
            const My::Integer<int> y = x.pow(e);
            CPPUNIT_ASSERT(y ==  My::Integer<int>("17179869184"));}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_pow6 () {
        try {
            const My::Integer<int> x = 2;
            const int              e = 61;
            const My::Integer<int> y = x.pow(e);
            CPPUNIT_ASSERT(y ==  My::Integer<int>("2305843009213693952"));}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_pow7 () {
        try {
            const My::Integer<int> x = 2;
            const int              e = 127;
            const My::Integer<int> y = x.pow(e);
            CPPUNIT_ASSERT(y ==  My::Integer<int>("170141183460469231731687303715884105728"));}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}


    // ----------------------
    // test_shift_left_digits
    // ----------------------

    void test_shift_left_digits () {
        const int a[] = {4, 3, 2};
        const int b[] = {0, 0, 4, 3, 2};
              int x[10];
        const int* p = My::shift_left_digits(a, a + 3, 2, x);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, b));}

    // -----------------------
    // test_shift_right_digits
    // -----------------------

    void test_shift_right_digits () {
        const int a[] = {4, 3, 5};
        const int b[] = {5};
              int x[10];
        const int* p = My::shift_right_digits(a, a + 3, 2, x);
        CPPUNIT_ASSERT(std::equal(const_cast<const int*>(x), p, b));}

    // --------
    // test_mult
    // --------

    void test_mult1 () {
        try {
            const My::Integer<int> x = 26;
            const int              y =  41;
            const My::Integer<int> z = x * y;
            CPPUNIT_ASSERT(x == 26);
            CPPUNIT_ASSERT(y ==  41);
            CPPUNIT_ASSERT(z ==  1066);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_mult2 () {
        try {
            const My::Integer<int> x = -25;
            const int              y =  4;
            const My::Integer<int> z = x * y;
            CPPUNIT_ASSERT(x == -25);
            CPPUNIT_ASSERT(y ==  4);
            CPPUNIT_ASSERT(z ==  -100);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_mult3 () {
        try {
            const My::Integer<int> x = -25;
            const int              y =  -4;
            const My::Integer<int> z = x * y;
            CPPUNIT_ASSERT(x == -25);
            CPPUNIT_ASSERT(y ==  -4);
            CPPUNIT_ASSERT(z ==  100);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}
    
    void test_mult4 () {
        try {
            My::Integer<int> x = -25;
            const My::Integer<int> z = (x *= x);
            CPPUNIT_ASSERT(x == 625);
            CPPUNIT_ASSERT(z ==  625);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}
    
    void test_mult5 () {
        try {
            My::Integer<int> x = 8454250;
            My::Integer<int> y = 8454250;
            x *= y;
            CPPUNIT_ASSERT(x == My::Integer<int>("71474343062500"));}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    // --------
    // test_shift
    // --------

    void test_leftshift () {
        try {
            const My::Integer<int> x = 265;
            const My::Integer<int> z = x << 2;
            CPPUNIT_ASSERT(x == 265);
            CPPUNIT_ASSERT(z ==  26500);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_rightshift1 () {
        try {
            const My::Integer<int> x = 265;
            const My::Integer<int> z = x >> 2;
            CPPUNIT_ASSERT(x == 265);
            CPPUNIT_ASSERT(z ==  2);}
        catch (std::invalid_argument& e) {
            CPPUNIT_ASSERT(false);}}

    void test_rightshift2 () {
            try {
                const My::Integer<int> x = 265;
                const My::Integer<int> z = x >> 5;
                CPPUNIT_ASSERT(x == 265);
                CPPUNIT_ASSERT(z ==  0);}
            catch (std::invalid_argument& e) {
                CPPUNIT_ASSERT(false);}}

    // --------
    // test_shift
    // --------

	void test_plusequals1() {
		My::Integer<int> x = 5;
		My::Integer<int> y = 1;
		CPPUNIT_ASSERT( (x += y) == 6); // x = 6
		CPPUNIT_ASSERT( (y += -x) == -5); // y = -5
	}

	void test_minusequals1() {
		My::Integer<int> x = 5;
		My::Integer<int> y = 1;
		CPPUNIT_ASSERT( (x -= y) == 4); // x = 4
		CPPUNIT_ASSERT( (y -= x) == -3); // y = -3
	}		
    // -----
    // suite
    // -----

    CPPUNIT_TEST_SUITE(TestInteger);

    CPPUNIT_TEST(test_constructor_1);
    CPPUNIT_TEST(test_constructor_2);
    CPPUNIT_TEST(test_constructor_3);
    CPPUNIT_TEST(test_constructor_4);
    CPPUNIT_TEST(test_constructor_5);
    CPPUNIT_TEST(test_constructor_6);
    CPPUNIT_TEST(test_constructor_7);
    CPPUNIT_TEST(test_constructor_8);
    CPPUNIT_TEST(test_abs);
    CPPUNIT_TEST(test_divides_digits1);
    CPPUNIT_TEST(test_divides_digits2);
    CPPUNIT_TEST(test_equal_to1);
    CPPUNIT_TEST(test_equal_to2);
    CPPUNIT_TEST(test_less_than1);
	CPPUNIT_TEST(test_less_than2);
    CPPUNIT_TEST(test_minus_digits1);
    CPPUNIT_TEST(test_minus_digits2);
    CPPUNIT_TEST(test_minus_digits3);
    CPPUNIT_TEST(test_minus_digits4);
    CPPUNIT_TEST(test_minus_digits5);
    CPPUNIT_TEST(test_minus_digits6);
    CPPUNIT_TEST(test_multiplies_digits);
    CPPUNIT_TEST(test_multiplies_digits1);
    CPPUNIT_TEST(test_multiplies_digits2);
    CPPUNIT_TEST(test_negation);
    CPPUNIT_TEST(test_output1);
    CPPUNIT_TEST(test_output2);
    CPPUNIT_TEST(test_plus_digits);
    CPPUNIT_TEST(test_plus_digits1);
    CPPUNIT_TEST(test_plus_digits2);
    CPPUNIT_TEST(test_plus_digits3);
    CPPUNIT_TEST(test_plus_digits4);
    CPPUNIT_TEST(test_plus_digits5);
    CPPUNIT_TEST(test_pow1);
    CPPUNIT_TEST(test_pow2);
    CPPUNIT_TEST(test_pow3);
    CPPUNIT_TEST(test_pow4);
    CPPUNIT_TEST(test_pow5);
    CPPUNIT_TEST(test_pow6);
    CPPUNIT_TEST(test_pow7);
    CPPUNIT_TEST(test_shift_left_digits);
    CPPUNIT_TEST(test_shift_right_digits);
    CPPUNIT_TEST(test_mult1);
    CPPUNIT_TEST(test_mult2);
    CPPUNIT_TEST(test_mult3); 
    CPPUNIT_TEST(test_mult4);
    CPPUNIT_TEST(test_mult5);
    CPPUNIT_TEST(test_leftshift); 
    CPPUNIT_TEST(test_rightshift1);
    CPPUNIT_TEST(test_rightshift2);
	CPPUNIT_TEST(test_plusequals1);
	CPPUNIT_TEST(test_minusequals1);
	
    CPPUNIT_TEST_SUITE_END();};

#endif // TestInteger_h

